\documentclass{report}[11pt]

\usepackage[french]{babel}
\usepackage[utf8]{inputenc}
\usepackage{color}
\usepackage{graphicx}
\usepackage{amsmath}

\begin{document}

\Large

\title{\textbf{Rapport Projet Réseau}}

\author{Mélanie Juan, Vincent Loubet \and Niels Montanari, Emmanuel Seunes }

\maketitle

\normalsize

\tableofcontents

\chapter{Introduction}

Ce projet consiste en le développement d'une application réseau pour le partage de fichiers en mode pair à pair.\\
\\
Il se déroule en deux parties :
\begin{itemize}
\item une version centralisée où une entité centrale, le tracker, assiste les pairs à la recherche de fichiers ainsi qu'à la recherche des pairs avec lequels communiquer pour télécharger les fichiers désirés ;
\item une version distribuée où les pairs trouvent par eux-mêmes les autres pairs constitutifs du réseau avec lesquels communiquer pour télécharger les fichiers désirés.\\
\end{itemize}

La version distribuée n'a pas été abordée par manque de temps et pour privilégier l'aboutissement et la qualité de la version centralisée.\\ Ce rapport présente donc l'implémentation de la version centralisée.\\
\\
Le choix du langage de programmation du tracker et des pairs a été décidé de la façon suivante :
\begin{itemize}
\item langage Java pour le tracker ;
\item langage C pour les pairs.\\
\end{itemize} 
L'implémentation des pairs étant estimée plus délicate à mettre en œuvre que celle du Tracker, nous avons privilégié le langage C pour les pairs, du fait de notre plus grande connaissance de ce langage.\\

Ce rapport est le rapport final associé à la soutenance du 7 mai 2012.\\

\chapter{Tracker}

Le travail au niveau du tracker s'est décomposé en trois gros morceaux :
\begin{itemize}
\item la création d'une base de données adéquate et des fonctions associées qui conviennent ;
\item la mise en place d'un serveur multi-threadé afin de gérer les différents clients ;
\item l'implémentation des fonctions de parsage permettant de récupérer les informations envoyées par les clients.\\
\end{itemize}

\begin{figure}[h]
   \caption{Diagramme de classe du tracker}
   \includegraphics[scale=0.4]{diagclass.eps}
\end{figure}

\section{Base de données}

La base de données et sa gestion portent de manière générale sur quatre classes : File, Host, Leecher, Seeder.


\begin{figure}[h]
   \caption{Principe de la base de données implémentée pour le tracker}
   \includegraphics[scale=0.4]{bdd.eps}
\end{figure}


La classe Host contient l'ip et le port d'écoute des clients, ce qui est nécessaire pour gérer les communications avec ces derniers.

La classe Leecher hérite de la classe Host et sert au référencement des clients nécessitant de télécharger un fichier donné.

De même la classe Seeder hérite de la classe Host et sert au référencement des clients pouvant partager un fichier donné.

La classe File contient les informations nécessaires (key, size, pieceSize) pour agencer complètement le transfert d'informations utiles à la communication entre les différents clients. Elle contient aussi deux hashtables, une pour les seeders et l'autre pour les leechers, afin de mémoriser les clients pouvant partager ou nécessitant un fichier donné.




\section{Serveur multi-threadé}

Le serveur multi-threadé commence par définir son port de connexion et créer un nouveau thread qui va gérer les commandes, puis ouvre un socket côté serveur. Le tracker se met ensuite en attente passive et crée un nouveau thread pour chaque client demandant l'établissement d'une connexion.



\section{Parseur des messages reçus}

Une fois la connexion au tracker effectuée, le nouveau thread lancé par le tracker va boucler en attente de message. Pour vérifier que le premier message reçu soit un message announce, on remplit le champ ip et port du client quand on reçoit ce message, puis le tracker vérifie aprés chaque message si l'ip et le port du client sont connus.

Le parsing en lui-même commence par la réception du message entier. En effet, comme les messages échangés entre le tracker et les clients ne sont pas volumineux, on peut se permettre de stocker en mémoire le message entier. 
On commence donc par parser le début du message pour savoir son type, suite à quoi, il est facile de savoir quand le message se terminera. 

Une fois le message totalement reçu, on vérifie que le message soit bien formé grâce à une expression régulière, ce qui permet d'avoir une vérification rapide et permissive quand à la forme du message. Ensuite, on découpe le message grâce à d'autres expressions régulières pour en retirer les informations pertinentes en vue de les traiter et d'envoyer le message correspondant. 

\section{Messages et comportements ambigus}

Plusieurs traitements de messages, et donc de réponses du tracker, ont dû être choisis de manière arbitraire suite à une certaine incompréhension / ambiguïté du sujet.

\begin{description}
\item[announce] Ce message et problématique pour le tracker, car si l'un des fichiers leech ne figure pas dans la base, on ne peut pas le créer par manque d'informations, telles la taille du fichier ou de ses pieces. Nous avons donc decidé d'ignorer ses fichiers leech et de ne pas les ajouter à la base. Ceci ne changera pas le fonctionnement global du programme, car un fichier leech seul ne peut finir de se télécharger si personne d'autre ne le partage.
\item[update] De la même façon que pour announce, il y a ici un problème de cohérence de la base. En effet, si un pair finit de télécharger un fichier, ce dernier va passer de leech à seed. Or, le tracker n'a aucun moyen de connaître le nom d'un fichier téléchargé et ce nom est nécessaire pour la recherche par nom. Nous supposons donc que le pair utilisera le même nom que le premier fichier seed ayant la même clé. Dans le cas où un unique pair (1) partage un fichier et un autre pair (2) le télécharge, il existe encore un cas problématique : si le pair (1) se déconnecte après la fin du téléchargement du fichier par le pair (2) mais avant l'envoi du nouvel update du pair (2), il n'y aura plus de nom à donner au nouveau fichier seed du pair (2) suite à l'envoi de l'update. Nous avons donc choisi de donner le nom "\_default" à ce fichier.
\end{description}



\chapter{Client}

Afin de communiquer avec le tracker, le client doit pouvoir récuperer la liste des fichiers qu'ils souhaitent partager afin de lui envoyer.\\
\\
Nous avons donc divisé le travail en deux parties : dans un premier temps, nous avons créé la fonction permettant d'établir la liste des fichiers à envoyer au tracker, puis dans un second temps, les communications avec le tracker et les autres pairs.\\

\section{Parcours de dossier}

Afin d'établir la liste des fichiers, il fallait créer des fonctions de gestion de listes afin de récupérer les sorties du parcours du dossier.\\
\\
Nous sommes aussi allés récupérer des fichiers permettant de générer une clé md5 afin d'avoir une identification unique des fichiers.\\
\\
Puis, nous avons implémenté le parcours de dossier.\\
Le parcours de dossier nous a posé quelques problèmes. En effet, nous avons eu du mal à récupérer la taille des fichiers contenus dans un dossier.\\
\\
Dans un premier temps, nous utilisions la fonction \textit{stat} mais le retour n'était pas satisfaisant. Notre encadrant nous a alors conseillé la fonction \textit{fseek} que nous avons eu du mal à mettre en place.\\
Effectivement, à l'éxécution, nous avions une erreur de segmentation que nous ne comprenions pas.\\
Après divers tests, nous avons compris qu'il ne trouvait pas les fichiers. Il y avait un problème de chemin lors de l'utilisation des fonctions.\\
\\
Une fois ce problème réglé, nous avions une liste des fichiers correspondante à ce qu'attendait le tracker, nous sommes donc passés à la partie communications.\\
\\
Nous sommes par la suite revenus sur cette partie car l'utilisation de \textit{fseek} se trouvait être une mauvaise idée dans le cas de gros fichier.\\ En effet, pour calculer la taille d'un fichier, nous utilisions la fonction \textit{fseek} pour parcourir l'ensemble du fichier et nous regardions le nombre d'octets lus.\\ Or, pour des fichier d'une taille de 2 Go, la fonction retournait la valeur au bout de plus de 30 secondes, ce qui était gênant pour l'envoi régulier d'update.\\ Nous sommes donc retournés à l'utilisation de la fonction \textit{stat} que nous avons finalement réussi à utiliser.\\  

\section{Communication avec le tracker et les autres pairs}

Une fonction a été implémentée pour chacune des communications possibles avec le tracker ou les autres pairs. Les informations à transmettre sont récupérées soit dans la liste des fichiers stockés localement, soit passées en paramètre de la fonction.\\
\\
Le buffermap est stocké sous la forme d'un tableau d'octets avec la taille du fichier et celle des pièces, de manière à toujours savoir quels sont les bits utiles du tableau. De cette façon, il est facile d'envoyer et de recevoir le buffermap via le réseau, car sa structure est déja bien formée pour l'envoi de message (type char *).\\ 
\\
La communication avec le tracker est gérée grâce à un thread qui est lancé au démarrage du programme et qui sera le seul à pouvoir communiquer directement avec le tracker.\\ Pour communiquer avec le tracker, les autres threads doivent envoyer un signal au thread\_tracker. Ce dernier va alors vérrouiller l'envoi de signaux grâce à un mutex et va ensuite regarder dans une variable globale la requête et ses paramètres.\\ Le thread\_tracker va ensuite envoyer la requête au tracker, recevoir la réponse, la placer dans une variable globale, envoyer un signal pour prévenir que la réponse est disponible et finalement déverrouiller l'envoi de signal de demande de requête.\\ De plus, pour garder la connexion TCP avec le tracker, le thread\_tracker va régulièrement envoyer des update s'il ne reçoit pas de demande de requête de la part des autres threads.\\
\\
Le fonctionnement de la communication entre pairs sera décrit dans la partie qui suit et dans la partie expliquant le déroulement d'un téléchargement.\\


\section{Couche TCP/IP}

Pour la couche TCP, nous avons utilisé les sockets, mécanisme d'interface de programmation permettant au programme d'échanger des données.\\

Chacun de nos pairs doit se comporter à la fois comme un client et un serveur. Il doit pouvoir s'allouer dynamiquement sur le système et se connecter au tracker pour pouvoir télécharger des fichiers, mais il doit aussi attendre les connexions afin de permettre le partage de ses fichiers.\\
\\
En mode connecté, les appels systèmes se déroulent selon ce schéma :
\begin{figure}[h]
   \caption{Schéma d'une communication en mode connecté}
   \includegraphics[scale=0.5]{sockets-images-connecte.eps}
\end{figure}

La communication par socket nécessite l'utilisation d'un descripteur afin d'identifier la connexion sur laquelle on envoie ou reçoit les données. Il faut donc utiliser une fonction nous permettant de récupérer un descripteur tout en créant le socket. Ce descripteur doit être unique car il sera utilisé comme identifiant de la connexion.

\subsection{Serveur}

Lorsque le pair se comporte comme le serveur, il doit respecter certaines règles.\\ Dans un premier temps, l'ouverture d'un socket se déroule en deux étapes :
\begin{itemize}
\item On utilise la fonction \textit{socket()} pour créer le socket et son descripteur.
\item On se sert de la fonction \textit{bind()} pour spécifier le type de communication associé au socket (protocole TCP ou UDP).\\
\end{itemize}

Dans un second temps, un serveur doit se mettre à l'écoute des messages éventuels. C'est à ce moment là qu'intervient la fonction \textit{listen()} en mode connecté (TCP). Il peut ensuite boucler sur l'attente passive de connexion avec les fonctions \textit{accept()}.\\
Une fois la demande de connexion détectée par accept, on vérifie que le serveur peut traiter la connexion grâce à un sémaphore qui donne le nombre de connexions simultanées disponibles. On alloue alors un emplacement mémoire au pair qui se connecte et on lance un thread qui va gérer cette connexion.\\
\\

Le fonctionnement du serveur repose donc sur l'algorithme :\\ 
\begin{verbatim}

Procedure serveur()

  entier des, client, numero_thread;
  des <- socket();           //On crée le socket et on récupère le descripteur
  bind(des);                 //On spécifie le type de communication
  listen(des);               //On place le socket en mode passif
  Tant que l'on peut gérer des connexions parallèles 
    client <- accept(des);   //On accepte la connexion d'un client
    Si(hasplace())           //On regarde si on a la place
       numero_thread <-rechercher_dans_tableau_place(client);//On donne la mémoire
       lancer_thread(client,numero_thread);//On lance le thread
    Sinon
      close(client);         //On ferme le socket
  fin Tant que;
  close(des);                //On ferme le socket

Procedure Thread(entier client,entier numero_client)

  Tant que le client est connecté     
    recv(client);            //On reçoit les données
    traitement(tableau[numero_client]);   
    send(client);            //On envoie des données
  fin Tant que;
  close(client);             //On ferme le socket
  liberer_espace_tableau(numero_client);

\end{verbatim}

\subsection{Client}

Lorsque le pair se comporte comme le client, le comportement est assez semblable à celui d'un serveur, avec de légères modifications.\\
\\
En effet, les fonctions \textit{bind()}, \textit{listen()} et \textit{accept()} n'ont pas de raison d'être là et sont remplacées par la fonction \textit{connect()} qui permet d'établir une connexion avec un serveur. En effet, pour établir une connexion, le client ne nécessite pas de faire un \textit{bind()}.\\
\\
Le fonctionnement du client repose donc sur l'algorithme :\\
\begin{verbatim}

Procedure client()

  entier des, retour;   
  des <- socket();       //On crée le socket et on récupère le descripteur
  connect(des);          //On établit la connexion
  send(des);             //On envoie des données
  recv(des);             //On reçoit des données
  close(des);            //On ferme le socket

\end{verbatim}

\chapter{Déroulement d'un téléchargement}

Pour pouvoir se connecter et télécharger des fichiers, il faut d'abord au préalable lancer le tracker.\\
On peut ensuite se connecter entre pairs grâce au tracker qui récupère les informations des différents pairs afin de pouvoir les mettre en contact.\\

\subsection*{Démarrage du Tracker}
\begin{verbatim}
Demarrage du Tracker sur le port : 18000
--------
Quitter : tapez "quit"
Nombre de connectes : tapez "total"
--------
\end{verbatim}

\subsection*{Démarrage d'un client}

Du côté du tracker :\\
\begin{verbatim}
Un nouveau client s'est connecte, no 0
Client no 0 : announce listen 16000 seed [] leech []
answer (port =16000) : ok

Client no 0 : update seed [] leech []
answer (port =16000) : ok
\end{verbatim}
Du côté du client :\\
\begin{verbatim}
Quel type de fichier cherchez vous ? (Entrer un nombre) :
---- ---- ----
(1) : Faire une recherche par nom
(2) : Faire une recherche par taille
(3) : Faire une recherche par nom et par taille
(4) : Quitter
---- ---- ----
\end{verbatim}

\subsection*{Fermeture d'un client}

Du côté du tracker :\\
\begin{verbatim}
Le client no 0 s'est deconnecte
\end{verbatim}
Du côté du client :\\
\begin{verbatim}
4
Fermeture de l'application ...
\end{verbatim}

\subsection*{Écrans de recherche du client}

Lorque l'on choisit la recherche par nom :\\
\begin{verbatim}
Quel est le nom complet du fichier que vous voulez télécharger ?:
---- ---- ----
(1) : Annuler cette recherche
(2) : Quitter
---- ---- ----
\end{verbatim}
Lorque l'on choisit la recherche par taille :\\
\begin{verbatim}
Quel est la taille minimale du fichier que vous voulez télécharger ?:
---- ---- ----
(1) : Annuler cette recherche
(2) : Quitter
---- ---- ----
\end{verbatim}
Quand on choisit la recherche par nom et par taille, on a successivement l'écran de recherche par taille puis celui de recherche par nom.\\
\\
Une fois le choix de la recherche effectué, l'envoi de la requête au tracker est effectué et on affiche la réponse. Suite à quoi, l'utilisateur peut choisir le fichier qu'il veut télécharger.\\
\\
Le client va alors initier le téléchargement en créant un fichier de sauvegarde pour stocker le buffermap si le programme se termine anormalement. Il crée aussi un nouveau fichier qui est remplit avec des octets aléatoires jusqu'a avoir la bonne taille. Il va finalement lancer autant de threads qu'il peut, dans la limite indiquée dans le fichier de configuration et du nombre de pairs partageant le fichier.\\
\\
Chaque thread se connecte à son pair et va demander le buffermap du fichier pour le comparer avec un buffermap temporaire commun à tous les threads.\\ Il demande ensuite les pièces qu'il n'a pas en commun avec le pair par groupe de 10 pour ne pas avoir à re-télécharger trop de pièces si le message contenant les pièces est corrompu. Une fois reçut un groupe de pièce, le thread va changer le buffermap du fichier et va éditer le fichier de sauvegarde.\\
\\
Le téléchargement finit quand le buffermap temporaire est plein. On va alors supprimer le fichier de sauvegarde, déplacer le fichier qui est maintenant complet dans le dossier données et notifier à l'utilisateur que le téléchargement est fini.\\

\chapter{Problèmes rencontrés}

L'un des premiers problèmes rencontrés fut celui du choix de la clef de hachage pour les tables du tracker.\\ Nous avons tout d'abord voulu utiliser des objets de type Host avec attributs, mais l'utilisation d'objet comme clef de hachage ne fonctionne pas. Nous avons donc utilisé une chaîne de caractères contenant les informations de l'objet Host (ip et port) comme clef de hachage pour résoudre ce problème.\\
\\
Nous avons par la suite eu des problèmes avec la fonction \textit{strtok}. Cette fonction permet de découper une chaîne de caractères selon une liste de caractères.\\ Elle permet de récupérer les differentes parties de la chaîne en l'appelant plusieurs fois mais avec le paramètre NULL à la place de la chaîne à découper, car la fonction va stocker en interne sa position dans la chaîne.\\ Or, l'endroit où est stocké ce pointeur d'avancement est le même quelque soit le thread.\\ Donc, même si nous utilisions cette fonction sur différentes chaîne de départ, seule la dernière était conservée, et chaque thread faisait avancer le pointeur de position de la fonction. \\Pour resoudre ce problème, il existe la fonction \textit{strtok\_r} que nous n'avons pas réussi à utiliser. Nous avons donc dû utiliser des décalages de pointeur à la place.\\
\\
Un des derniers problèmes que nous avons eu fut la détection des déconnections des autres pairs.\\ En effet, si un pair se déconnecte quand on lui envoie des données, le socket le détecte et retourne une valeur d'erreur.\\ À l'inverse, quand nous attendons des données d'un pair, la déconnexion du pair n'entraîne pas d'erreur de la part de notre socket.\\ Ceci entraîne l'impossibilité de savoir s'il faut rechercher de nouveaux pairs pour continuer le téléchargement ou simplement notifier à l'utilisateur que le téléchargement ne peut continuer par manque de pair.\\ Une solution simple serait de mettre en place des timers qui retournent une erreur si le pair met trop de temps à répondre, comme cela fut fait pour le thread qui gère la connection au tracker. Hélas, par manque de temps, nous n'avons pas pu résoudre ce problème.\\

\chapter{Conclusion}

Au final, nous avons crée une application qui :
\begin{enumerate}
\item dispose d'une interface utilisateur en shell ;
\item permet les téléchargements parallèles ;
\item permet le téléchargement depuis un pair qui est lui-même en train de télécharger le fichier désiré ;
\item permet la reprise des téléchargements interrompus ;
\item permet la reprise automatique des téléchargements lors du lancement de l'application;
\item permet les connexions multiples entre le tracker et des clients ;
\item gère le déplacement dans le dossier données des fichiers dont le téléchargement vient de se finir.\\
\end{enumerate}

Il reste cependant quelques points imparfaits qui pourraient être améliorés :
\begin{enumerate}
\item La gestion du fichier de configuration est très basique et peu lisible.
\item On ne sait pas si un téléchargement est interrompu.
\item L'interface utilisateur mériterait un affichage de l'avancement des differents téléchargements.
\item Il manque des timers lors de la réception de messages pour pouvoir détecter la déconnexion d'un pair.
\item Il manque aussi la mise à jour des buffermap entre pairs. 
\end{enumerate}

\end{document}
